home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / faq / amigafaq.lha / AmigaFAQ / src / addtoc.c
C/C++ Source or Header  |  1994-07-28  |  22KB  |  992 lines

  1. /*
  2.     addtoc.c    V1.1    25.07.1994
  3.  
  4.     Copyright (C)   1993    Jochen Wiedmann
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20.  
  21.     This scans a texinfo file and adds a table of contents to the relating
  22.     AmigaGuide and Ascii files. Note that they have to be created first and
  23.     MUST be up to date. Best way to ensure this is using a Makefile.
  24.  
  25.     Usage:  addtoc TFILE/A,SPLITCHAP/M/N,GFILE/K,DFILE/K,DIFFS/K
  26.  
  27.     TFILE is the texinfo source, GFILE is the AmigaGuide file and DFILE is
  28.     the Ascii file. Files GFILE.new and DFILE.new are created.
  29.  
  30.     SPLITCHAP are numbers indicating chapters, after which to split the
  31.     FAQ into different parts. (Each part will be preceded by a toc.) This
  32.     is available for Ascii files only and is used to split FAQs in different
  33.     parts. Note, that these numbers must be adjacent, for example the
  34.     numbers 7 and 11 to split after chapter 6 and 10.
  35.  
  36.     DIFFS is the name of a file holding diffs to a previous version. This
  37.     must be produced using "gdiff -f new old". When creating the Ascii
  38.     version, changed or added lines will be marked with a "!" or "+",
  39.     respectively, together with the sections they belong to. 
  40.  
  41.     The texinfo file has to be in a special format: Each node (except the Top
  42.     node) MUST have a following line containing an @chapter, @section,
  43.     @subsection or @unnumbered command. The node commands MUST contain
  44.     nothing else than the node name. Note that @info and @tex commands are
  45.     ignored. In fact anything is ignored, except for the node and sectioning
  46.     commands.
  47.  
  48.     Author:      Jochen Wiedmann
  49.           Am Eisteich 9
  50.         72555 Metzingen (Germany)
  51.           Tel. 07123 / 14881
  52.  
  53.  
  54.     Computer:      Amiga 1200 (should run on any Amiga)
  55.  
  56.     Compiler:      Dice and Aztec-C (should run through SAS and gcc)
  57.  
  58.  
  59.     V1.0        25.08.1993
  60.  
  61.     V1.1        25.07.1994        Added support of @include
  62. */
  63.  
  64. #include <stdlib.h>
  65. #include <stdio.h>
  66. #include <string.h>
  67. #include <errno.h>
  68. #ifndef FALSE
  69. #define FALSE 0
  70. #endif
  71. #ifndef TRUE
  72. #define TRUE (!FALSE)
  73. #endif
  74. #ifndef HAVE_STRICMP
  75. #define stricmp strcasecmp
  76. #define strnicmp strncasecmp
  77. #endif
  78.  
  79.  
  80. /*
  81.     Maximum length of a line in the source file. Probably works with longer
  82.     lines, but this may not be guaranteed. Even the line numbering WILL
  83.     be damaged.
  84. */
  85. #define MAXLINE 1024    /*  Maximum length of a node name.  */
  86.  
  87.  
  88. /*
  89.     This is used to hold the information we get by scanning the texinfo
  90.     source. Each node is represented by exactly one node structure.
  91. */
  92. struct node
  93. { struct node *next;
  94.   char *name;
  95.   char *title;
  96.   int type;
  97.   int changed;
  98. };
  99. #define TYPE_UNNUMBERED 0
  100. #define TYPE_CHAPTER    1
  101. #define TYPE_SECTION    2
  102. #define TYPE_SUBSECTION 3
  103.  
  104. /*
  105.     This is used to store the diffs information.
  106. */
  107. struct diffs
  108. { struct diffs *next;
  109.   int type;
  110.   int from, to;
  111. };
  112. #define DIFFTYPE_DELETED 0
  113. #define DIFFTYPE_CHANGED 1
  114. #define DIFFTYPE_ADDED 2
  115.  
  116.  
  117.  
  118. /*
  119.     The ignorespace() function removes trailing blanks from a line.
  120. */
  121. char *ignorespace(char *line)
  122.  
  123. { while (*line == ' '  ||  *line == '\t')
  124.   { ++line;
  125.   }
  126.   return(line);
  127. }
  128.  
  129.  
  130.  
  131.  
  132. /*
  133.     The salloc() function allocates memory for a string. Note, that it
  134.     removes trailing Line-Feeds.
  135. */
  136. char *salloc(char *str)
  137.  
  138. { char *ptr;
  139.   int len = strlen(str);
  140.  
  141.   while (len > 0  &&  str[len-1] == '\n')
  142.   { str[--len] = '\0';
  143.   }
  144.   if ((ptr = malloc(len+1))  !=  NULL)
  145.   { strcpy(ptr, str);
  146.   }
  147.   return(ptr);
  148. }
  149.  
  150.  
  151.  
  152.  
  153. /*
  154.     The memerror() function reports a memory error and terminates the
  155.     program.
  156. */
  157. void memerror(void)
  158.  
  159. { fprintf(stderr, "Out of memory!\n");
  160.   exit(20);
  161. }
  162.  
  163.  
  164.  
  165.  
  166. /*
  167.    This is a subfunction of Scan() which is used to support @inclde:
  168.    It calls itself to do this.
  169. */
  170. void ScanFILE(FILE *fh, char *filename, struct node **first)
  171.  
  172. { extern int errno;
  173.   char line[MAXLINE+1];
  174.   char title[MAXLINE+1];
  175.   char *titleptr;
  176.   struct node *node;
  177.   int lineno = 0;
  178.  
  179.   while (fgets(line, sizeof(line), fh)  !=  NULL)
  180.   { ++lineno;
  181.     if (strnicmp(line, "@include", 8) == 0)
  182.     { char *includefilename = strtok(line+8, " \t\n\r\f");
  183.       FILE *includefh;
  184.  
  185.       if (!(includefh = fopen(includefilename, "r")))
  186.       { fprintf(stderr, "Cannot open %s as source file!\n", filename);
  187.     exit(10);
  188.       }
  189.  
  190.       ScanFILE(includefh, includefilename, first);
  191.       fclose(includefh);
  192.     }
  193.     else if (strnicmp(line, "@node", 5)  ==  0           &&
  194.     fgets(title, sizeof(title), fh)  !=  NULL)
  195.     { int type;
  196.  
  197.       ++lineno;
  198.       type = -1;
  199.       if (strnicmp(title, "@unnumbered", 11)  ==  0)
  200.       { type = TYPE_UNNUMBERED;
  201.     titleptr = title+11;
  202.       }
  203.       else if (strnicmp(title, "@chapter", 8)  ==  0)
  204.       { type = TYPE_CHAPTER;
  205.     titleptr = title+8;
  206.       }
  207.       else if (strnicmp(title, "@section", 8)  ==  0)
  208.       { type = TYPE_SECTION;
  209.     titleptr = title+8;
  210.       }
  211.       else if (strnicmp(title, "@subsection", 11)  ==  0)
  212.       { type = TYPE_SUBSECTION;
  213.     titleptr = title+11;
  214.       }
  215.       else if (strnicmp(title, "@top", 4)  !=  0)
  216.       { fprintf(stderr, "%s, %d, warning: Unknown sectioning command.\n",
  217.         lineno);
  218.     fprintf(stderr, 
  219.         "         Expected @chapter, @section, @subsection, @unnumbered or @top.\n");
  220.       }
  221.       if (type != -1)
  222.       { if ((node = calloc(1, sizeof(*node)))  ==  NULL)
  223.     { memerror();
  224.     }
  225.     if ((node->name = salloc(ignorespace(line+5)))  ==  NULL   ||
  226.         (node->title = salloc(ignorespace(titleptr)))  ==  NULL)
  227.     { memerror();
  228.     }
  229.     node->next = NULL;
  230.     node->type = type;
  231.  
  232.     /* 
  233.        Look for the last node in the current list and add the
  234.        current node.
  235.     */
  236.     { struct node **last;
  237.  
  238.       for (last = first;  *last != NULL;
  239.            last = &((*last)->next))
  240.       {
  241.       }
  242.       *last = node;
  243.     }
  244.       }
  245.     }
  246.   }
  247.  
  248.   if (errno)
  249.   { perror("addtoc");
  250.   }
  251. }
  252.  
  253.  
  254.  
  255.  
  256. /*
  257.     The Scan() function scans the texinfo source file. It uses
  258.     ScanFILE in order to work recursively.
  259. */
  260. void Scan(struct node **first, char *tfile)
  261.  
  262. { FILE *fh;
  263.  
  264.   if ((fh = fopen(tfile, "r"))  ==  NULL)
  265.   { fprintf(stderr, "Cannot open %s as source file!\n", tfile);
  266.     exit(10);
  267.   }
  268.  
  269.   *first = NULL;
  270.  
  271.   ScanFILE(fh, tfile, first);
  272.  
  273.   fclose(fh);
  274. }
  275.  
  276.  
  277.  
  278.  
  279. /*
  280.     The myscan() function scans a string like @{"title" Link "name"} for
  281.     name.
  282. */
  283. char *myscan(char *line, char *name)
  284.  
  285. { line = ignorespace(line);
  286.   if (strncmp(line, "@{\"", 3)  !=  0)
  287.   { return(NULL);
  288.   }
  289.   line += 3;
  290.   while (*line != '\"'  &&  *line != '\0')
  291.   { line++;
  292.   }
  293.   if (*line == '\0')
  294.   { return(NULL);
  295.   }
  296.   ++line;
  297.   line = ignorespace(line);
  298.   if (strnicmp(line, "Link", 4)  !=  0)
  299.   { return(NULL);
  300.   }
  301.   line+=4;
  302.   line = ignorespace(line);
  303.   if (*(line++) != '\"')
  304.   { return(NULL);
  305.   }
  306.   while (*line != '\"'  &&  *line != '\0')
  307.   { *(name++) = *(line++);
  308.   }
  309.   if (strncmp(line, "\"}", 2)  !=  0)
  310.   { return(NULL);
  311.   }
  312.   *name = '\0';
  313.   return(line+2);
  314. }
  315.  
  316.  
  317.  
  318.  
  319. /*
  320.     The subcmp() function checks for len occurences of c. Result is 0, if
  321.     there are, nonzero otherwise.
  322. */
  323. int subcmp(char *line, char c, int len)
  324.  
  325. { int i;
  326.  
  327.   for (i = 0;  i < len;  i++)
  328.   { if (line[i] != c)
  329.     { return(line[i]-c);
  330.     }
  331.   }
  332.   return(0);
  333. }
  334.  
  335.  
  336.  
  337.  
  338. /*
  339.     This function is used to read the diffs. These consist of lines like
  340.         am
  341.         cm n
  342.         dm n
  343.     where a means "following lines added after line m", c equals to "lines
  344.     m to n changed" and d is "lines m to n deleted". (n may be omitted, if
  345.     n == m.)
  346.  
  347.     a and c lines are followed by the appropriate number of lines to add or
  348.     change, followed by a line with a point only.
  349.  
  350.     When the lines are read, the doc file is scanned a first time to find,
  351.     which nodes are changed.
  352. */
  353. void ScanDiffs(struct diffs **first, char *file, char *docfile,
  354.            struct node *firstnode)
  355.  
  356. { FILE *fhin;
  357.   int linenr = 0;
  358.   char line[MAXLINE+1];
  359.   char subline[MAXLINE+1];
  360.   struct diffs *newdiffs;
  361.   struct diffs **first_diff = first;
  362.   struct node *currentnode = NULL;
  363.  
  364.   /*
  365.      Opening the diffs file.
  366.   */
  367.   if ((fhin = fopen(file, "r"))  ==  NULL)
  368.     { fprintf(stderr, "Cannot open diffs file %s.\n", file);
  369.       exit(10);
  370.     }
  371.  
  372.   while(fgets(line, sizeof(line), fhin)  !=  NULL)
  373.     { char *ptr2, *ptr = line;
  374.  
  375.       ++linenr;
  376.       if (*ptr != 'd'  &&  *ptr != 'a'  &&  *ptr != 'c')
  377.     { fprintf(stderr, 
  378.           "diffs file %s has inappropriate format in line %d.\n",
  379.           file, linenr);
  380.       exit(10);
  381.     }
  382.  
  383.       if ((newdiffs = calloc(1, sizeof(*newdiffs)))  ==  NULL)
  384.     { fprintf(stderr, "Out of memory.");
  385.       exit(10);
  386.     }
  387.       newdiffs->from = strtol(++ptr, &ptr, 10);
  388.       if (ptr == line+1)
  389.     { fprintf(stderr,
  390.           "diffs file %s has inappropriate format in line %d.\n",
  391.           file, linenr);
  392.       exit(10);
  393.     }
  394.       newdiffs->to = strtol(ptr, &ptr2, 10);
  395.       if (ptr2 == ptr)
  396.     { newdiffs->to = newdiffs->from;
  397.     }
  398.  
  399.       switch(line[0])
  400.     { /*
  401.          "Delete" chunk
  402.       */
  403.       case 'd':
  404.         newdiffs->type = DIFFTYPE_DELETED;
  405.         break;
  406.       /* 
  407.          "Add" chunk
  408.       */
  409.       case 'a':
  410.         newdiffs->type = DIFFTYPE_ADDED;
  411.         newdiffs->to = newdiffs->from-1;
  412.         /*
  413.            Find, how much lines to add by reading them.
  414.         */
  415.         for(;;)
  416.           { if (fgets(line, sizeof(line), fhin)  ==  NULL)
  417.           { fprintf(stderr,
  418.                 "Unexpected end of diffs file %s.\n", file);
  419.             exit(10);
  420.           }
  421.         if (strcmp(".\n", line)  ==  0)
  422.           { break;
  423.           }
  424.         newdiffs->to++;
  425.           }
  426.           
  427.         if (newdiffs->to < newdiffs->from)
  428.           { fprintf(stderr,
  429.             "Diffs file %s has inappropriate format in line %d.\n",
  430.             file, linenr);
  431.         exit(10);
  432.           }
  433.         break;
  434.       /*
  435.          "Change" chunk
  436.       */
  437.       case 'c':
  438.         newdiffs->type = DIFFTYPE_CHANGED;
  439.         
  440.         /*
  441.            Skip the following lines
  442.         */
  443.         for(;;)
  444.           { if (fgets(line, sizeof(line), fhin)  ==  NULL)
  445.           { fprintf(stderr,
  446.                 "Unexpected end of diffs file %s.\n", file);
  447.             exit(10);
  448.           }
  449.         if (strcmp(".\n", line)  ==  0)
  450.           { break;
  451.           }
  452.           }
  453.           break;
  454.       }
  455.       *first = newdiffs;
  456.       first = &(newdiffs->next);
  457.     }
  458.   fclose(fhin);
  459.  
  460.  
  461.   /*
  462.       Opening the doc file.
  463.   */
  464.   if ((fhin = fopen(docfile, "r"))  ==  NULL)
  465.   { fprintf(stderr, "Cannot open %s as input!\n", docfile);
  466.     exit(10);
  467.   }
  468.  
  469.  
  470.   /*
  471.       Scanning for nodes
  472.   */
  473.   linenr = 0;
  474.   currentnode = NULL;
  475.   while (fgets(line, sizeof(line), fhin)  !=  NULL)
  476.   { struct diffs *d;
  477.  
  478.     ++linenr;
  479.     if (firstnode != NULL  &&
  480.     strncmp(line, firstnode->title, strlen(firstnode->title))  ==  0   &&
  481.     fgets(subline, sizeof(subline), fhin)  !=  NULL)
  482.     { char c;
  483.       static char subchar[3] = "*=-";
  484.  
  485.       ++linenr;
  486.       switch(firstnode->type)
  487.       { case TYPE_UNNUMBERED:
  488.     case TYPE_CHAPTER:
  489.       c = subchar[0];
  490.       break;
  491.     case TYPE_SECTION:
  492.       c = subchar[1];
  493.       break;
  494.     case TYPE_SUBSECTION:
  495.       c = subchar[2];
  496.       break;
  497.       }
  498.       if(subcmp(subline, c, strlen(firstnode->title))  ==  0)
  499.       { /*
  500.        Node found!
  501.     */
  502.     currentnode = firstnode;
  503.     firstnode = firstnode->next;
  504.     continue;
  505.       }
  506.  
  507.     }
  508.     if (currentnode  !=  NULL)
  509.       { for (d = *first_diff;  d != NULL;  d = d->next)
  510.       { if (d->from <= linenr  &&  d->to >= linenr)
  511.           { /*
  512.            Adding of empty lines should not make the section being
  513.            marked as changed.
  514.         */
  515.         if (d->type == DIFFTYPE_DELETED)
  516.           { char *ptr = line;
  517.  
  518.             while(*ptr != '\0')
  519.               { if (*ptr != ' '  &&  *ptr != '\t'  &&
  520.                 *ptr != '\r'  &&  *ptr != '\n')
  521.               { break;
  522.               }
  523.             ++ptr;
  524.               }
  525.             if (*ptr == '\0')
  526.               { continue;
  527.               }
  528.           }
  529.         currentnode->changed = TRUE;
  530.           }
  531.       }
  532.       }
  533.   }
  534.  
  535.   if (firstnode != NULL)
  536.   { fprintf(stderr, "Missing item, probably different text in header "
  537.             "and menu:\n%s\n", firstnode->title);
  538.   }
  539.   if (errno)
  540.   { perror("addtoc");
  541.   }
  542.   fclose(fhin);
  543. }
  544.  
  545.  
  546.  
  547.  
  548.  
  549. /*
  550.     The ScanGuide() function scans the AmigaGuide file, removes the menu
  551.     in the top node and replaces it by the table of contents.
  552. */
  553. void ScanGuide(struct node *first, char *gtitle)
  554.  
  555. { FILE *fhin;
  556.   FILE *fhout;
  557.   char *newtitle;
  558.   struct node *node;
  559.   int InMain;
  560.   int lineno;
  561.   char line[MAXLINE+1];
  562.   char name[MAXLINE+1];
  563.  
  564.   /*
  565.       Opening files
  566.   */
  567.   if ((newtitle = malloc(strlen(gtitle)+5))  ==  NULL)
  568.   { memerror();
  569.   }
  570.   sprintf(newtitle, "%s.new", gtitle);
  571.  
  572.   if ((fhin = fopen(gtitle, "r"))  ==  NULL)
  573.   { fprintf(stderr, "Cannot open %s as input!\n", gtitle);
  574.     exit(10);
  575.   }
  576.   if ((fhout = fopen(newtitle, "w"))  ==  NULL)
  577.   { fprintf(stderr, "Cannot open %s as output!\n", newtitle);
  578.     exit(11);
  579.   }
  580.  
  581.   /*
  582.       Looking for the Top Node
  583.   */
  584.   InMain = FALSE;
  585.   lineno = 0;
  586.   while(fgets(line, sizeof(line), fhin)  !=  NULL)
  587.   { ++lineno;
  588.     if (strnicmp(line, "@Node Main", 10)  ==  0)
  589.     { InMain = TRUE;
  590.     }
  591.     else if (strnicmp(line, "@EndNode", 8)  ==  0)
  592.     { InMain = FALSE;
  593.     }
  594.     if (InMain  &&  strnicmp(ignorespace(line), "@{\"", 3) == 0)
  595.     { if (myscan(line, name)  ==  NULL)
  596.       { fprintf(stderr,
  597.         "Error: Cannot scan line %d of %s (unknown format)!\n",
  598.         lineno, gtitle);
  599.       }
  600.       else
  601.       { int blanks;
  602.  
  603.     fputs(line, fhout);
  604.     blanks = ignorespace(line)-line;
  605.     for (node = first;  node != NULL;  node = node->next)
  606.     { if (strncmp(name, node->name, strlen(name)) == 0  &&
  607.           (node->type == TYPE_CHAPTER  ||  node->type == TYPE_UNNUMBERED))
  608.       { break;
  609.       }
  610.     }
  611.  
  612.     node = node->next;
  613.     while (node != NULL  &&  node->type != TYPE_CHAPTER  &&
  614.            node->type != TYPE_UNNUMBERED)
  615.     { switch (node->type)
  616.       { case TYPE_UNNUMBERED:
  617.         case TYPE_CHAPTER:
  618.           break;
  619.         case TYPE_SECTION:
  620.           fprintf(fhout, "    ");
  621.           break;
  622.         case TYPE_SUBSECTION:
  623.           fprintf(fhout, "        ");
  624.           break;
  625.       }
  626.       fprintf(fhout, "@{\" %s \" Link \"%s\"}\n", node->title,
  627.           node->name);
  628.       node = node->next;
  629.     }
  630.       }
  631.     }
  632.     else
  633.     { fputs(line, fhout);
  634.     }
  635.   }
  636.   if (errno)
  637.   { perror("addtoc");
  638.   }
  639.   fclose(fhin);
  640.   fclose(fhout);
  641. }
  642.  
  643.  
  644.  
  645.  
  646. /*
  647.    adddoctoc adds the table of contents to the given ascii file.
  648. */
  649. void adddoctoc(FILE *fhout, struct node *first)
  650. { int chapter, section, subsection;
  651.   struct node *node;
  652.  
  653.   chapter = section = subsection = 0;
  654.   for (node = first;  node != NULL;  node = node->next)
  655.   { if (node->type == TYPE_CHAPTER  ||  node->type == TYPE_UNNUMBERED)
  656.     { putc('\n', fhout);
  657.     }
  658.     fprintf(fhout, node->changed ? "! " : "  ");
  659.     switch (node->type)
  660.     { case TYPE_CHAPTER:
  661.     ++chapter;
  662.     section = subsection = 0;
  663.     fprintf(fhout, "%d ", chapter);
  664.     break;
  665.       case TYPE_UNNUMBERED:
  666.     break;
  667.       case TYPE_SECTION:
  668.     ++section;
  669.     subsection = 0;
  670.     fprintf(fhout, "  %d ", section);
  671.     break;
  672.       case TYPE_SUBSECTION:
  673.     ++subsection;
  674.     fprintf(fhout, "    %d ", subsection);
  675.     break;
  676.       }
  677.     fprintf(fhout, "%s\n", node->title);
  678.   }
  679.   fprintf(fhout, "\n\n\n");
  680. }
  681.  
  682.  
  683.  
  684.  
  685. /*
  686.    diffputs is like fputs, but recognizes diffs.
  687. */
  688. void diffputs(char *line, FILE *fhout, struct diffs *first_diff, int linenr)
  689.  
  690.   {
  691.     while (first_diff != NULL)
  692.       { if (first_diff->from <= linenr  &&  first_diff->to >= linenr)
  693.       { switch(first_diff->type)
  694.           { case DIFFTYPE_DELETED:
  695.           fprintf(fhout, "+ %s", line);
  696.           return;
  697.         case DIFFTYPE_CHANGED:
  698.           fprintf(fhout, "! %s", line);
  699.           return;
  700.         default:
  701.           if (linenr == first_diff->from)
  702.             { fprintf(fhout, "< %s", line);
  703.               return;
  704.             }
  705.         }
  706.       }
  707.     first_diff = first_diff->next;
  708.       }
  709.     fprintf(fhout, "  %s", line, fhout);
  710.   }
  711.  
  712.  
  713.  
  714. /*
  715.     The ScanDoc function scans the Ascii document and adds the table of
  716.     contents and section numbers.
  717. */
  718. void ScanDoc(struct node *first, char *dtitle, int *splitchaps,
  719.          struct diffs *first_diff)
  720.  
  721. { FILE *fhin, *fhout;
  722.   struct node *initialfirst = first;
  723.   char *newtitle;
  724.   int lineno;
  725.   int chapter, section, subsection;
  726.   int tocdone;
  727.   int splitnum = 0;
  728.   char line[MAXLINE+1];
  729.   char subline[MAXLINE+1];
  730.   static char subchar[3] = "*=-";
  731.   char c;
  732.  
  733.   /*
  734.       Opening files
  735.   */
  736.   if ((newtitle = malloc(strlen(dtitle)+20))  ==  NULL)
  737.   { memerror();
  738.   }
  739.   sprintf(newtitle, "%s.%d", dtitle, splitnum+1);
  740.  
  741.   if ((fhin = fopen(dtitle, "r"))  ==  NULL)
  742.   { fprintf(stderr, "Cannot open %s as input!\n", dtitle);
  743.     exit(10);
  744.   }
  745.   if ((fhout = fopen(newtitle, "w"))  ==  NULL)
  746.   { fprintf(stderr, "Cannot open %s as output!\n", newtitle);
  747.     exit(11);
  748.   }
  749.  
  750.  
  751.   /*
  752.       Scanning for nodes
  753.   */
  754.   tocdone = FALSE;
  755.   lineno = 0;
  756.   chapter = section = subsection = 0;
  757.   while (fgets(line, sizeof(line), fhin)  !=  NULL)
  758.   { ++lineno;
  759.     if (first != NULL  &&
  760.     strncmp(line, first->title, strlen(first->title))  ==  0   &&
  761.     fgets(subline, sizeof(subline), fhin)  !=  NULL)
  762.     { ++lineno;
  763.       switch(first->type)
  764.       { case TYPE_UNNUMBERED:
  765.     case TYPE_CHAPTER:
  766.       c = subchar[0];
  767.       break;
  768.     case TYPE_SECTION:
  769.       c = subchar[1];
  770.       break;
  771.     case TYPE_SUBSECTION:
  772.       c = subchar[2];
  773.       break;
  774.       }
  775.       if(subcmp(subline, c, strlen(first->title))  ==  0)
  776.       { /*
  777.        Node found!
  778.      */
  779.     int i;
  780.     int level;
  781.     struct node *node;
  782.     char number[128];
  783.  
  784.     /*
  785.        Split the file, if its a chapter node with the current splitting
  786.        number.
  787.     */
  788.         if (first->type == TYPE_CHAPTER  &&  splitchaps  &&
  789.         splitchaps[splitnum] == chapter+1)
  790.     { fclose(fhout);
  791.       sprintf(newtitle, "%s.%d", dtitle, ++splitnum+1);
  792.  
  793.       if ((fhout = fopen(newtitle, "w"))  ==  NULL)
  794.       { fprintf(stderr, "Cannot open %s as output!\n", newtitle);
  795.         exit(11);
  796.       }
  797.       adddoctoc(fhout, initialfirst);
  798.     }
  799.       
  800.  
  801.     /*
  802.         Add the table of contents, if we have found the first
  803.         node.
  804.     */
  805.     if (!tocdone)
  806.     { adddoctoc(fhout, initialfirst);
  807.       tocdone = TRUE;
  808.     }
  809.  
  810.     switch(first->type)
  811.     { case TYPE_UNNUMBERED:
  812.         strcpy(number, "");
  813.         level = 0;
  814.         break;
  815.       case TYPE_CHAPTER:
  816.         ++chapter;
  817.         section = subsection = 0;
  818.         sprintf(number, "%d ", chapter);
  819.         level = 0;
  820.         break;
  821.       case TYPE_SECTION:
  822.         ++section;
  823.         subsection = 0;
  824.         sprintf(number, "%d.%d ", chapter, section);
  825.         level = 1;
  826.         break;
  827.       case TYPE_SUBSECTION:
  828.         ++subsection;
  829.         sprintf(number, "%d.%d.%d ", chapter, section, subsection);
  830.         level = 2;
  831.         break;
  832.     }
  833.     fprintf(fhout, "%s%s%s%s", first->changed ? "! " : "  ",
  834.         number, line, first->changed ? "! " : "  ");
  835.     for (i = 0;  i < strlen(number);  i++)
  836.     { putc((int) subchar[level], fhout);
  837.     }
  838.     fputs(subline, fhout);
  839.     first = first->next;
  840.       }
  841.       else
  842.       { diffputs(line, fhout, first_diff, lineno-1);
  843.     diffputs(subline, fhout, first_diff, lineno);
  844.       }
  845.     }
  846.     else
  847.     { diffputs(line, fhout, first_diff, lineno);
  848.     }
  849.   }
  850.   if (first != NULL)
  851.   { fprintf(stderr, "Missing item, probably different text in header "
  852.             "and menu:\n%s\n", first->title);
  853.   }
  854.   if (errno)
  855.   { perror("addtoc");
  856.   }
  857.   fclose(fhin);
  858.   fclose(fhout);
  859. }
  860.  
  861.  
  862.  
  863.  
  864. /*
  865.     This prints the usage information and terminates.
  866. */
  867. void Usage(void)
  868.  
  869. { fprintf(stderr,
  870.     "Usage: addtoc TFILE/A,SPLITCHAPS/M/N,GFILE/K,DFILE/K\n\n"
  871.     "TFILE is the texinfo source file.\n"
  872.     "GFILE is the AmigaGuide file created from TFILE and DFILE the "
  873.     "corresponding\n"
  874.     "Ascii file. <GFILE>.new and <TFILE>.new, respectively, are created,\n"
  875.     "if the latter options are present.\n"
  876.     "SPLITCHAPS are chapter numbers, before which the file should be "
  877.         "splitted in\n"
  878.     "different parts, for example 7 11 to split after chapters 6 and 10. "
  879.         "(ascii file only)\n\n");
  880.   exit(1);
  881. }
  882.  
  883.  
  884.  
  885.  
  886. /*
  887.     This is main(). Does nothing except processing the arguments and calling
  888.     the Scan... functions.
  889. */
  890. void main(int argc, char *argv[])
  891.  
  892. { char *tfile, *gfile, *dfile, *diffs_file;
  893.   struct node *first;
  894.   struct diffs *first_diff;
  895.   int i;
  896.   int numsplitchaps;
  897.   int *splitchaps;
  898.  
  899. #ifdef DEBUG
  900.   tfile = "/Amiga-FAQ.texinfo";
  901.   gfile = "/Amiga-FAQ.guide";
  902.   dfile = "/Amiga-FAQ.doc";
  903. #else
  904.   if (argc < 2)
  905.   { Usage();
  906.   }
  907.  
  908.   splitchaps = NULL;
  909.   tfile = NULL;
  910.   gfile = NULL;
  911.   dfile = NULL;
  912.   diffs_file = NULL;
  913.  
  914.   for (i = 1;  i < argc;  i++)
  915.   { if (stricmp(argv[i], "gfile")  ==  0)
  916.     { if (++i == argc  ||  gfile != NULL)
  917.       { Usage();
  918.       }
  919.       gfile = argv[i];
  920.     }
  921.     else if (strnicmp(argv[i], "gfile=", 6)  ==  0)
  922.     { if (gfile != NULL)
  923.       { Usage();
  924.       }
  925.       gfile = &argv[i][6];
  926.     }
  927.     else if (stricmp(argv[i], "dfile")  ==  0)
  928.     { if (++i == argc  ||  dfile != NULL)
  929.       { Usage();
  930.       }
  931.       dfile = argv[i];
  932.     }
  933.     else if (strnicmp(argv[i], "dfile=", 6)  ==  0)
  934.     { if (dfile != NULL)
  935.       { Usage();
  936.       }
  937.       dfile = &argv[i][6];
  938.     }
  939.     else if (tfile == NULL)
  940.     { tfile = argv[i];
  941.     }
  942.     else if (strnicmp(argv[i], "diffs=", 6)  ==  0)
  943.     { if (diffs_file != NULL)
  944.       { Usage();
  945.       }
  946.       diffs_file = &argv[i][6];
  947.     }
  948.     else if (stricmp(argv[i], "diffs") == NULL)
  949.     { if (++i == argc  ||  diffs_file != NULL)
  950.       { Usage();
  951.       }
  952.       diffs_file = argv[i];
  953.     }
  954.     else
  955.     { int *oldsplitchaps = splitchaps;
  956.  
  957.       if (splitchaps == NULL)
  958.       { numsplitchaps = 0;
  959.       }
  960.       { if ((splitchaps = malloc(sizeof(int)*(numsplitchaps+2)))  ==  NULL)
  961.     { memerror();
  962.     }
  963.       }
  964.       if (numsplitchaps > 0)
  965.       { memcpy(splitchaps, oldsplitchaps, sizeof(int)*numsplitchaps);
  966.     free(oldsplitchaps);
  967.       }
  968.       if ((splitchaps[numsplitchaps++] = atoi(argv[i]))  <=  0)
  969.       { fprintf(stderr, "Chapter number must be > 0.\n");
  970.     exit(10);
  971.       }
  972.       splitchaps[numsplitchaps] = 0;
  973.     }
  974.   }
  975.   if (tfile == NULL)
  976.   { Usage();
  977.   }
  978. #endif    /*  !DEBUG   */
  979.  
  980.  
  981.   Scan(&first, tfile);
  982.   if (gfile)
  983.   { ScanGuide(first, gfile);
  984.   }
  985.   if (dfile)
  986.   { if (diffs_file)
  987.     { ScanDiffs(&first_diff, diffs_file, dfile, first);
  988.     }
  989.     ScanDoc(first, dfile, splitchaps, first_diff);
  990.   }
  991. }
  992.